package org.wjk.handler;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.wjk.exception.FailureMsg;
import org.wjk.pojo.vo.ResponseRes;
import org.wjk.service.SysUserSvs;
import org.wjk.utils.JedisUtils;
import org.wjk.utils.ResponseUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.ThreadPoolExecutor;

@Component
@Slf4j
@RequiredArgsConstructor
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    private final JedisUtils jedisUtils;
    private final SysUserSvs userSvs;
    private final ThreadPoolExecutor executor;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        ResponseRes res = null;
        /*登录失败：
         * 返回登录失败信息数据
         * {statusCode: 5001, message: ""}*/
        /*InternalAuthenticationServiceException抛出的时机：
        AOP拦截了UserDetailsService::loadUserByName()方法后，该方法抛出异常后，被catch了，又重新抛出异常后，
        Springsecurity抛出InternalAuthenticationServiceException异常！
        */
        if (exception instanceof InternalAuthenticationServiceException)
            res = ResponseRes.failure(5001, "您输入的用户名对应的账号不存在！", null);
        else if (exception instanceof BadCredentialsException) {
            /*查redis，有没有以当前用户名为key，错误次数，获取错误次数，判断，redis里错误次数是否<2
             * 如果小于2，为对应key的值增加1，同时，响应客户端“您输入的密码错误！”
             * 如果=2，重新开线程，锁定该账号，
             * redis里，删除对应key的值。*/
            /*1.获取用户名*/
            String key = request.getParameter("username");
            log.debug("当前用户名为{}", key);
            /*2.以该key去获取Redis保存的错误次数。*/
            String failureCounts = jedisUtils.get(key);
            if (failureCounts == null || Integer.parseInt(failureCounts) < 2) {
                jedisUtils.incr(key);
                res = ResponseRes.failure(5001, "您输入的密码错误！", null);
            } else {
                /*重新启线程禁用该账号*/
                executor.execute(() -> {
                    userSvs.lockedCurrentUser(key);
                });
                /*删除当前用户名key,redis中缓存的错误次数。*/
                jedisUtils.delete(key);
                res = ResponseRes.failure(5001, "您输入的密码错误次数过多，您的账号被禁用！", null);
            }

            /*TODO:用户输入密码错误三次，锁定该用户账号*/
        } else if (exception instanceof CredentialsExpiredException)
            res = ResponseRes.failure(5001, "您的凭证已过期！", null);
        else if (exception instanceof DisabledException)
            res = ResponseRes.failure(5001, "您的账号未启用，请联系管理员！", null);
        else if (exception instanceof LockedException)
            res = ResponseRes.failure(5001, "您的账号已锁定，请联系管理员！", null);
        else if (exception instanceof AccountExpiredException)
            res = ResponseRes.failure(5001, "您的账号已过期！", null);
        else
            res = ResponseRes.failure(FailureMsg.LOGIN_FAILURE);

        ResponseUtils.convertToJson(response, res);
    }
}